package com.sdsy.blockchaintrainingback.service.training;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sdsy.blockchaintrainingback.context.ThreadLocalContextHolder;
import com.sdsy.blockchaintrainingback.enums.CommonStatusEnum;
import com.sdsy.blockchaintrainingback.exception.ServiceException;
import com.sdsy.blockchaintrainingback.mapper.network.NetworkMapper;
import com.sdsy.blockchaintrainingback.mapper.training.*;
import com.sdsy.blockchaintrainingback.pojo.network.Network;
import com.sdsy.blockchaintrainingback.pojo.training.*;
import com.sdsy.blockchaintrainingback.service.docker.DockerService;
import com.sdsy.blockchaintrainingback.vo.training.CreateVncContainerVo;
import com.sdsy.blockchaintrainingback.vo.training.SaveAnswerReportVo;
import com.sdsy.blockchaintrainingback.vo.training.UserTrainingContainerVo;
import com.sdsy.blockchaintrainingback.vo.training.UserTrainingRespVo;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

import static com.sdsy.blockchaintrainingback.exception.enums.ErrorCodeConstants.NETWORK_NOT_EXIST;
import static com.sdsy.blockchaintrainingback.exception.enums.ErrorCodeConstants.USER_NOT_BIND_TRAINING;

/**
 * @Description TODO
 * @Author linzili
 * @Date 2023/9/26 23:31
 * @Version 1.0
 */
@Service
@Transactional
public class UserTrainingServiceImpl implements UserTrainingService {

    @Resource
    private UserTrainingMapper userTrainingMapper;
    @Resource
    private UserTrainingContainerRelationMapper userTrainingContainerMapper;

    @Resource
    private DockerService dockerService;

    @Resource
    private TrainingMapper trainingMapper;
    @Resource
    private EnvironmentMapper environmentMapper;

    @Resource
    private TrainingEnvRelationMapper trainingEnvRelationMapper;

    @Resource
    private UserTrainingReportRelationMapper userTrainingReportRelationMapper;

    @Resource
    private NetworkMapper networkMapper;

    @Resource
    private UserTrainingRelationMapper userTrainingRelationMapper;

    @Override
    public UserTrainingRespVo startTraining(Long userId, Integer trainingId) {
        if (userId != 1) {
            UserTrainingRelation userTrainingRelation = userTrainingRelationMapper.selectOne(UserTrainingRelation::getUserId, userId, UserTrainingRelation::getTrainingId, trainingId);
            if (userTrainingRelation == null) {
                throw new ServiceException(USER_NOT_BIND_TRAINING);
            }
        }
        // 1. 检查是否有正在进行的实训
        UserTraining currentTraining = userTrainingMapper.findActiveTrainingByUserId(userId);
        // 1.1 如果有并且不是要开启的实训则关闭
        if (currentTraining != null) {
            if (!currentTraining.getTrainingId().equals(trainingId)) {
                stopContainer(currentTraining);
                currentTraining.setStatus(CommonStatusEnum.DISABLE.getStatus());
                userTrainingMapper.updateById(currentTraining);
            } else {
                // 1.2 如果有且是目标实训 则返回实训信息
                return getUserTrainingInfo(currentTraining);
            }
        }
        // 2. 检查是否是第一次开启实训
        UserTraining userTraining = userTrainingMapper.selectOne(UserTraining::getUserId, userId, UserTraining::getTrainingId, trainingId);
        if (userTraining == null) {
            // 2.1 如果是则创建实训
            userTraining = createUserTraining(userId, trainingId);
        } else {
            // 2.2如果不是则查询实训信息 开启容器并返回
            startContainer(userTraining);
            userTraining.setStatus(CommonStatusEnum.ENABLE.getStatus());
            userTrainingMapper.updateById(userTraining);
        }
        return getUserTrainingInfo(userTraining);
    }

    /**
     * 保存答案
     * @param vo
     */
    @Override
    public void saveAnswerReport(SaveAnswerReportVo vo) {
        UserTrainingReportRelation userTrainingReportRelation =
                userTrainingReportRelationMapper.selectOne(UserTrainingReportRelation::getTrainingId, vo.getTrainingId(),
                        UserTrainingReportRelation::getUserId, ThreadLocalContextHolder.getUserId());
        userTrainingReportRelation.setAnswerReport(vo.getAnswerReport());
        userTrainingReportRelationMapper.updateById(userTrainingReportRelation);
    }

    @Override
    public void restartContainer(Integer userTrainingId) {
        List<UserTrainingContainerRelation> userTrainingContainerRelations = userTrainingContainerMapper.selectList(UserTrainingContainerRelation::getUserTrainingId,userTrainingId);
        for (UserTrainingContainerRelation userTrainingContainerRelation : userTrainingContainerRelations) {
            dockerService.restartContainer(userTrainingContainerRelation.getContainerId());
        }
    }

    @Override
    public void resetContainer(Integer userTrainingId) {
        List<UserTrainingContainerRelation> userTrainingContainerRelations = userTrainingContainerMapper.selectList(UserTrainingContainerRelation::getUserTrainingId, userTrainingId);
        for (UserTrainingContainerRelation userTrainingContainerRelation : userTrainingContainerRelations) {
            dockerService.removeContainer(userTrainingContainerRelation.getContainerId());
            userTrainingContainerMapper.deleteById(userTrainingContainerRelation);
        }
        UserTraining userTraining = userTrainingMapper.selectById(userTrainingId);
        List<TrainingEnvRelation> trainingEnvRelations = trainingEnvRelationMapper.selectList(TrainingEnvRelation::getTrainingId, userTraining.getTrainingId());
        Training training = trainingMapper.selectById(userTraining.getTrainingId());
        Network network = networkMapper.selectById(training.getNetworkId());
        // 1.2 遍历环境列表
        for (TrainingEnvRelation trainingEnvRelation : trainingEnvRelations) {
            // 1.3 根据数量创建容器
            for (int i = 0; i < trainingEnvRelation.getContainerCount(); i++) {
                Environment environment = environmentMapper.selectById(trainingEnvRelation.getEnvId());
                UserTrainingContainerRelation userTrainingContainerRelation = new UserTrainingContainerRelation();
                userTrainingContainerRelation.setUserTrainingId(userTraining.getId());
                userTrainingContainerRelation.setType(environment.getType());
                if (i != 0) {
                    userTrainingContainerRelation.setContainerName(trainingEnvRelation.getContainerName() + (i + 1));
                } else {
                    userTrainingContainerRelation.setContainerName(trainingEnvRelation.getContainerName());
                }
                CreateVncContainerVo container = createContainer(environment.getTag(), environment.getVersion(), environment.getType(), network.getNetworkId());
                userTrainingContainerRelation.setContainerId(container.getContainerId());
                userTrainingContainerRelation.setContainerPort(container.getContainerPort());
                userTrainingContainerRelation.setContainerVncPassword(container.getContainerVncPassword());
                userTrainingContainerMapper.insert(userTrainingContainerRelation);
            }
        }
    }


    /**
     * 获取用户实训详情
     *
     * @param currentTraining
     * @return
     */
    private UserTrainingRespVo getUserTrainingInfo(UserTraining currentTraining) {
        UserTrainingRespVo userTrainingRespVo = new UserTrainingRespVo();
        Training training = trainingMapper.selectById(currentTraining.getTrainingId());
        userTrainingRespVo.setTrainingName(training.getName());
        userTrainingRespVo.setTaskFile(training.getTaskFile());
        userTrainingRespVo.setId(currentTraining.getId());

        UserTrainingReportRelation userTrainingReportRelation = userTrainingReportRelationMapper.selectOne(UserTrainingReportRelation::getUserTrainingId, currentTraining.getId());
        userTrainingRespVo.setAnswerReport(userTrainingReportRelation.getAnswerReport());

        List<UserTrainingContainerRelation> userTrainingContainerRelations = userTrainingContainerMapper.selectList(UserTrainingContainerRelation::getUserTrainingId, currentTraining.getId());

        List<UserTrainingContainerVo> userTrainingContainerVos = new ArrayList<>();
        for (UserTrainingContainerRelation userTrainingContainerRelation : userTrainingContainerRelations) {
            UserTrainingContainerVo userTrainingContainerVo = new UserTrainingContainerVo();
            BeanUtil.copyProperties(userTrainingContainerRelation, userTrainingContainerVo);
            userTrainingContainerVos.add(userTrainingContainerVo);
        }
        userTrainingRespVo.setContainerList(userTrainingContainerVos);
        return userTrainingRespVo;
    }

    /**
     * 创建用户实训
     *
     * @param userId
     * @param trainingId
     */
    private UserTraining createUserTraining(Long userId, Integer trainingId) {
        // 初始化用户实训
        UserTraining userTraining = new UserTraining();
        userTraining.setTrainingId(trainingId);
        userTraining.setUserId(userId);
        userTraining.setStatus(CommonStatusEnum.ENABLE.getStatus());
        userTrainingMapper.insert(userTraining);

        // 获取网络
        Training training = trainingMapper.selectById(trainingId);
        Network network = networkMapper.selectById(training.getNetworkId());
        if (network == null) throw new ServiceException(NETWORK_NOT_EXIST);
        // 初始化用户实训容器
        // 1.1 获取环境列表
        List<TrainingEnvRelation> trainingEnvRelations = trainingEnvRelationMapper.selectList(TrainingEnvRelation::getTrainingId, trainingId);
        // 1.2 遍历环境列表
        for (TrainingEnvRelation trainingEnvRelation : trainingEnvRelations) {
            // 1.3 根据数量创建容器
            for (int i = 0; i < trainingEnvRelation.getContainerCount(); i++) {
                Environment environment = environmentMapper.selectById(trainingEnvRelation.getEnvId());
                UserTrainingContainerRelation userTrainingContainerRelation = new UserTrainingContainerRelation();
                userTrainingContainerRelation.setUserTrainingId(userTraining.getId());
                userTrainingContainerRelation.setType(environment.getType());
                if (i != 0) {
                    userTrainingContainerRelation.setContainerName(trainingEnvRelation.getContainerName() + (i + 1));
                } else {
                    userTrainingContainerRelation.setContainerName(trainingEnvRelation.getContainerName());
                }
                CreateVncContainerVo container = createContainer(environment.getTag(), environment.getVersion(), environment.getType(), network.getNetworkId());
                userTrainingContainerRelation.setContainerId(container.getContainerId());
                userTrainingContainerRelation.setContainerPort(container.getContainerPort());
                userTrainingContainerRelation.setContainerVncPassword(container.getContainerVncPassword());
                userTrainingContainerMapper.insert(userTrainingContainerRelation);
            }
        }
        // 初始化答题报告
        UserTrainingReportRelation userTrainingReportRelation = new UserTrainingReportRelation();
        userTrainingReportRelation.setUserTrainingId(userTraining.getId());
        userTrainingReportRelation.setTrainingId(trainingId);
        userTrainingReportRelation.setUserId(userId);
        userTrainingReportRelation.setAnswerReport(training.getAnswerArea());
        userTrainingReportRelationMapper.insert(userTrainingReportRelation);
        return userTraining;
    }

    /**
     * 创建容器
     *
     * @param tag
     * @param version
     * @param type
     * @param networkId
     * @return
     */
    private CreateVncContainerVo createContainer(String tag, String version, Integer type, String networkId) {
        String imageName = tag + ":" + version;
        if (type == 0) {
            return dockerService.createVncContainer(imageName, networkId);
        }
        return null;
    }


    /**
     * 开启容器
     *
     * @param currentTraining
     */
    private void startContainer(UserTraining currentTraining) {
        List<UserTrainingContainerRelation> userTrainingContainerRelations = userTrainingContainerMapper.selectList(UserTrainingContainerRelation::getUserTrainingId, currentTraining.getId());
        for (UserTrainingContainerRelation userTrainingContainerRelation : userTrainingContainerRelations) {
            dockerService.startContainer(userTrainingContainerRelation.getContainerId());
        }

    }

    /**
     * 关闭容器
     *
     * @param currentTraining
     */
    private void stopContainer(UserTraining currentTraining) {
        List<UserTrainingContainerRelation> userTrainingContainerRelations = userTrainingContainerMapper.selectList(UserTrainingContainerRelation::getUserTrainingId, currentTraining.getId());
        for (UserTrainingContainerRelation userTrainingContainerRelation : userTrainingContainerRelations) {
            dockerService.stopContainer(userTrainingContainerRelation.getContainerId());
        }

    }
}
